using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Design;
using System.Text;

using InfFront.UICore;

namespace InfFront.InfoSources.Base
{
    /// <summary>
    /// Base IInfoCommand implementation
    /// </summary>
    [ToolboxItem(false), DesignTimeVisible(false)]
    public class InfoCommand : IInfoCommand, IComponent, IDisposable, IInfoSourceOwnedObject
    {
        #region Private members
        CommandStereotype stereotype;
        private bool enabled = true;
        private InfoSource owner;
        private string name = string.Empty;
        private string title = string.Empty;
        private string comment = string.Empty;
        private string defaultExecutionMessage = "Please wait...";
        private int    order = 99;
        private CommandEnablingStrategy enablingStrategy = CommandEnablingStrategy.AlwaysEnabled;
        private CommandCallMethod preferredCallMethod = CommandCallMethod.Synchronous;
        private ISite site;
        private IconStack icons;
        private EventHandlerList events;
        #endregion

        #region Initialization/Construction
        public InfoCommand()
        {
            Init();
        }

        public InfoCommand(IContainer container)
        {
            container.Add(this);
            Init();
        }

        private void Init()
        {
            icons = new IconStack();
        }
        #endregion Initialization/Construction

        #region Public properties

        [Browsable(false)]
        public InfoSource Owner
        {
            get { return owner; }
        }

        void IInfoSourceOwnedObject.SetOwner(InfoSource value)
        {
            owner = value;
        }

        [Category("General"), DefaultValue(CommandStereotype.Undefined)]
        public CommandStereotype Stereotype
        {
            get { return stereotype; }
            set { stereotype = value; }
        }

        [Category("General"), DefaultValue(true)]
        public bool Enabled
        {
            get { return enabled; }
            set { enabled = value; }
        }

        [Category("General"), Editor("InfFront.InfoSources.Base.Design.IconStackEditor, InfFront.InfoSources.Base.Design", typeof(UITypeEditor)), MergableProperty(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public IconStack Icons
        {
            get { return icons; }
            set { icons = value; }
        }

        [Category("General"), DefaultValue(99), Browsable(false)]
        public int Order
        {
            get { return order; }
            set { order = value; }
        }

        [Browsable(false)]
        public string Name
        {
            get
            {
                string result = name;
                if (string.IsNullOrEmpty(result))
                    result = Site.Name;
                if (result == null)
                    result = string.Empty;
                return result;
            }
            set
            {
                name = value;
                if (name == null)
                    name = string.Empty;
            }
        }

        [Category("General"), DefaultValue("")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        [Category("General"), DefaultValue("")]
        public string Comment
        {
            get { return comment; }
            set { comment = value; }
        }

        [Category("General"), DefaultValue("Please wait...")]
        public string DefaultExecutionMessage
        {
            get { return defaultExecutionMessage; }
            set { defaultExecutionMessage = value; }
        }

        [DefaultValue(CommandEnablingStrategy.AlwaysEnabled)]
        public CommandEnablingStrategy EnablingStrategy
        {
            get { return enablingStrategy; }
            set { enablingStrategy = value; }
        }

        [DefaultValue(CommandCallMethod.Synchronous)]
        public CommandCallMethod PreferredCallMethod
        {
            get { return preferredCallMethod; }
            set { preferredCallMethod = value; }
        }

        #endregion Public properties

        #region Public Events

        private static object EVENT_EXECUTE = new object();

        public event InfoCommandExecHandler OnExecute
        {
            add { Events.AddHandler(EVENT_EXECUTE, value); }
            remove { Events.RemoveHandler(EVENT_EXECUTE, value); }
        }

        private static object EVENT_ENABLECHECK = new object();

        public event InfoCommandEnableCheckHandler OnEnableCheck
        {
            add { Events.AddHandler(EVENT_ENABLECHECK, value); }
            remove { Events.RemoveHandler(EVENT_ENABLECHECK, value); }
        }

        private static object EVENT_DISPOSE = new object();

        public event EventHandler Disposed
        {
            add { Events.AddHandler(EVENT_DISPOSE, value); }
            remove { Events.RemoveHandler(EVENT_DISPOSE, value); }
        }

        #endregion

        #region IInfoCommand Members

        string IInfoCommand.GetName()
        {
            return Name;
        }

        string IInfoCommand.GetTitle()
        {
            return Title;
        }

        string IInfoCommand.GetComment()
        {
            return Comment;
        }

        int IInfoCommand.GetOrder()
        {
            return Order;
        }

        CommandEnablingStrategy IInfoCommand.GetEnablingStrategy()
        {
            return EnablingStrategy;
        }

        CommandCallMethod IInfoCommand.GetPreferredCallMethod()
        {
            return PreferredCallMethod;
        }

        IImage IInfoCommand.GetIcon(IInfoCommandGetIconArgs args)
        {
            var stream = Icons.GetStretched(args.GetHSize(), args.GetVSize(), args.GetStyle());
            return new ImageRecord() { Data = stream };
        }

        #endregion

        #region IDisposable Members
        void IDisposable.Dispose()
        {
            lock (this)
            {
                if ((site != null) && (site.Container != null))
                {
                    site.Container.Remove(this);
                }
                if (events != null)
                {
                    var handler = (EventHandler)Events[EVENT_DISPOSE];
                    if (handler != null)
                        handler(this, EventArgs.Empty);
                    events.Dispose();
                    events = null;
                }
            }
        }

        #endregion

        #region IComponent Members

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
        public ISite Site
        {
            get { return site; }
            set { site = value; }
        }

        #endregion

        #region Execution

        public class Execution : IExecutionControl
        {
            private InfoCommand command;
            private IExecuteCommandArgs args;

            public Execution(InfoCommand command, IExecuteCommandArgs args)
            {
                this.command = command;
                this.args = args;
                this.IsCancelled = false;
                this.Message = command.DefaultExecutionMessage;
                this.MaxProgress = 100;
                this.Progress = -1;
            }

            public bool IsCancelled { get; set; }

            public string Message { get; set; }

            public string ProgressMessage { get; set; }

            [DefaultValue(100)]
            public int MaxProgress { get; set; }

            [DefaultValue(-1)]
            public int Progress { get; set; }

            #region IInfoCommandExecution Members

            void IExecutionControl.Execute()
            {
                command.Execute(args.GetCurrentEntry(), this);
            }

            void IExecutionControl.Cancel()
            {
                IsCancelled = true;
            }

            string IExecutionControl.GetExecutionMessage()
            {
                return Message;
            }

            float IExecutionControl.GetExecutionProgress()
            {
                return this.Progress / this.MaxProgress;
            }

            string IExecutionControl.GetExecutionProgressMessage()
            {
                return ProgressMessage;
            }

            void IExecutionControl.GetExecutionProgressAndMessage(out float progress, out string message)
            {
                progress = (float) this.Progress / this.MaxProgress;
                message = ProgressMessage;
            }

            #endregion
        }

        internal IExecutionControl StartExecution(IExecuteCommandArgs args)
        {
            return new Execution(this, args);
        }

        internal void Execute(IInfoEntry currentEntry, InfoCommand.Execution exec)
        {
            try
            {
                var handler = (InfoCommandExecHandler)Events[EVENT_EXECUTE];
                if (handler != null)
                {
                    var args = new InfoCommandExecArgs(Owner.UnwrapEntry(currentEntry), currentEntry, exec);
                    using (var batch = new CallbackMessageBatch(Owner.RootOwner))
                    {
                        handler(this, args);
                    }
                }
            }
            catch (System.Exception ex)
            {
                var newEx = new InfoCommandException(this, ex.Message, ex);
                newEx.Source = string.Format("'{0}' ('{1}') command execution", Name, Title);
                throw newEx;
            }
        }

        #endregion Execution

        #region Internal Members

        internal bool CheckEnabled(object currentEntry)
        {
            switch (EnablingStrategy)
            {
                case CommandEnablingStrategy.AlwaysDisabled:
                    return false;
                case CommandEnablingStrategy.AlwaysEnabled:
                    return true;
                case CommandEnablingStrategy.AlwaysEnabledWhenThereIsCurrentEntry:
                    return (currentEntry != null);
                case CommandEnablingStrategy.AlwaysEnabledWhenThereIsEntries:
                    return Owner.HasEntries();
                case CommandEnablingStrategy.NeedsEnableCheck:
                    if (currentEntry != null)
                    {
                        var handler = (InfoCommandEnableCheckHandler)Events[EVENT_ENABLECHECK];
                        if (handler == null)
                            return false;
                        var args = new InfoCommandEnableCheckArgs();
                        args.CurrentItem = currentEntry;
                        handler(this, args);
                        return args.Enabled;
                    }
                    return false;
                default:
                    throw new NotImplementedException(string.Format("Unknown command enabling strategy: {0}", EnablingStrategy));
            }

        }

        #endregion

        #region Private Members

        protected EventHandlerList Events
        {
            get
            {
                if (events == null)
                {
                    events = new EventHandlerList();
                }
                return events;
            }
        }

        #endregion Private Members
    }

    public class InfoCommandExecArgs : EventArgs
    {
        object curItem;
        IInfoEntry curEntry;
        InfoCommand.Execution exec;

        public InfoCommandExecArgs(object curItem, IInfoEntry curEntry, InfoCommand.Execution exec)
        {
            this.curItem = curItem;
            this.curEntry = curEntry;
            this.exec = exec;
        }

        public object CurrentItem { get { return curItem; } }
        public IInfoEntry CurrentEntry { get { return curEntry; } }

        public InfoCommand.Execution Execution { get { return exec; } }
    }

    public delegate void InfoCommandExecHandler(object sender, InfoCommandExecArgs args);

    public class InfoCommandEnableCheckArgs : EventArgs
    {
        public InfoCommandEnableCheckArgs()
        {
            Enabled = false;
        }

        public object CurrentItem { get; set; }

        [DefaultValue(false)]
        public bool Enabled { get; set; }
    }

    public delegate void InfoCommandEnableCheckHandler(object sender, InfoCommandEnableCheckArgs args);

    /// <summary>
    /// InfoCommand stereotype enumeration
    /// </summary>
    public enum CommandStereotype
    {
        Undefined,
        Insert,
        Update,
        Edit,
        Delete,
        Accept,
        Cancel,
        NavigateForward,
        NavigateBack,
        NavigateUp,
        NavigateDown,
        NavigateInside,
        NavigateAway
    }

    /// <summary>
    /// Base InfoCommand container
    /// </summary>
    public class InfoCommandList : InfoSourceOwnedListOf<InfoCommand>
    {
        public InfoCommandList(InfoSource owner)
            : base(owner)
        {
        }

        public class InterfaceContainer : Container<IInfoCommand>, IInfoCommandList
        {
        }
    }
}
